#include "helper.h"

#ifndef M_WIN
#include <semaphore.h>
#endif

#include "../include/semaphore.h"

namespace mix
{

struct SemImpl
{
#ifdef M_WIN
    HANDLE sem;
#else
    sem_t sem;
#endif
    bool inited;
};

#define IMPL ((SemImpl*)_impl)
#define IMPL_VAR(si) SemImpl* si = (SemImpl*)_impl;

Semaphore::Semaphore() {
    SemImpl* si = new SemImpl();
    _impl = si;
#ifdef M_WIN
    si->sem = 0;
#else
#endif
    si->inited = false;
}

Semaphore::~Semaphore() {
    close();
    delete IMPL;
    _impl = NULL;
}

bool Semaphore::init( int initCount /*= 0 */, int maxCount) {
    IMPL_VAR( si);
    if ( si->inited)
        return false;
#ifdef M_WIN
    if ( maxCount <= 0)
        maxCount = 10000;
    si->sem = ::CreateSemaphore( NULL, initCount, maxCount, NULL);
    if ( si->sem == 0)
        return false;
    else {
        si->inited = true;
        return true;
    }
#else
    if ( sem_init( &(si->sem), 0, initCount))
        return false;
    else{
        si->inited = true;
        return true;
    }
#endif
}

void Semaphore::close() {
    IMPL_VAR( si);
    if ( si->inited) {
#ifdef M_WIN
        ::CloseHandle( si->sem);
        si->sem = 0;
#else
        ::sem_destroy( &(si->sem));
#endif
        si->inited = false;
    }
}

void Semaphore::wait() {
    IMPL_VAR( si);
    assert( si->inited);
#ifdef M_WIN
    ::WaitForSingleObject( si->sem, INFINITE);
#else
    ::sem_wait( &(si->sem));
#endif
}

bool Semaphore::tryWait() {
    IMPL_VAR( si);
    assert( si->inited);
#ifdef M_WIN
    if ( ::WaitForSingleObject( si->sem, INFINITE) == WAIT_OBJECT_0)
        return true;
    else
        return false;
#else
    if ( ::sem_trywait( &(si->sem)) == 0)
        return true;
    else
        return false;
#endif
}

bool Semaphore::tryWait( long msec) {
    IMPL_VAR( si);
    assert( si->inited);
    if ( msec < 0 || !si->inited)
        return false;
#ifdef M_WIN
    if ( ::WaitForSingleObject( si->sem, msec) == WAIT_OBJECT_0)
        return true;
    else
        return false;
#else
    struct timespec ts;
    if ( !futureAbsTime( msec, ts))
        return false;

    if ( ::sem_timedwait( &(si->sem), &ts) == 0)
        return true;
    else
        return false;
#endif
}

bool Semaphore::post() {
    IMPL_VAR( si);
    if ( !si->inited)
        return false;
#ifdef M_WIN
    if ( ::ReleaseSemaphore( si->sem, 1, NULL))
        return true;
    else
        return false;
#else
    if ( ::sem_post( &(si->sem)))
        return false;
    else
        return true;
#endif
}

void* Semaphore::getNative() {
    IMPL_VAR( si);
    return &(si->sem);
}

}   // namespace mix
